基于request cache请求缓存技术优化批量商品数据查询接口

基于request cache请求缓存技术优化批量商品数据查询接口

我们上一讲讲解的那个图片,顺着那个图片的流程,来一个一个的讲解hystrix的核心技术

1、创建command,2种command类型
2、执行command,4种执行方式
3、查找是否开启了request cache,是否有请求缓存,如果有缓存,直接取用缓存,返回结果

首先,有一个概念,叫做reqeust context,请求上下文,一般来说,在一个web应用中,hystrix

我们会在一个filter里面,对每一个请求都施加一个请求上下文,就是说,tomcat容器内,每一次请求,就是一次请求上下文

然后在这次请求上下文中,我们会去执行N多代码,调用N多依赖服务,有的依赖服务可能还会调用好几次

在一次请求上下文中,如果有多个command,参数都是一样的,调用的接口也是一样的,其实结果可以认为也是一样的

那么这个时候,我们就可以让第一次command执行,返回的结果,被缓存在内存中,然后这个请求上下文中,后续的其他对这个依赖的调用全部从内存中取用缓存结果就可以了

不用在一次请求上下文中反复多次的执行一样的command,提升整个请求的性能

HystrixCommand和HystrixObservableCommand都可以指定一个缓存key,然后hystrix会自动进行缓存,接着在同一个request context内,再次访问的时候,就会直接取用缓存

用请求缓存,可以避免重复执行网络请求

多次调用一个command,那么只会执行一次,后面都是直接取缓存

对于请求缓存(request caching),请求合并(request collapsing),请求日志(request log),等等技术,都必须自己管理HystrixReuqestContext的声明周期

在一个请求执行之前,都必须先初始化一个request context

HystrixRequestContext context = HystrixRequestContext.initializeContext();

然后在请求结束之后,需要关闭request context

context.shutdown();

一般来说,在java web来的应用中,都是通过filter过滤器来实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* hystrix上下文过滤器
*/
public class HystrixRequestContextFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HystrixRequestContext hystrixRequestContext = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest,servletResponse);
} catch (Exception e) {
e.printStackTrace();
}finally {
hystrixRequestContext.shutdown();
}
}
@Override
public void destroy() {
}
}
//注册
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new HystrixRequestContextFilter());
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
//调用
for (String productId : productIds.split(",")) {
HystrixCommand<ProductInfo> command = new GetProductInfoCommand(Long.valueOf(productId));
ProductInfo productInfo = command.execute();
System.out.println(productInfo);
System.out.println(command.isResponseFromCache());
}
//结果
调用接口,查询商品数据,productId=1
ProductInfo [id=1, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
false
调用接口,查询商品数据,productId=2
ProductInfo [id=2, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
false
调用接口,查询商品数据,productId=3
ProductInfo [id=3, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
false
ProductInfo [id=1, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
true
ProductInfo [id=2, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
true
ProductInfo [id=3, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=null, brandName=null]
true

结合咱们的业务背景,我们做了一个批量查询商品数据的接口,在这个里面,我们其实通过HystrixObservableCommand一次性批量查询多个商品id的数据

但是这里有个问题,如果说nginx在本地缓存失效了,重新获取一批缓存,传递过来的productId都没有进行去重,1,1,2,2,5,6,7

那么可能说,商品id出现了重复,如果按照我们之前的业务逻辑,可能就会重复对productId=1的商品查询两次,productId=2的商品查询两次

我们对批量查询商品数据的接口,可以用request cache做一个优化,就是说一次请求,就是一次request context,对相同的商品查询只能执行一次,其余的都走request cache

request cache的原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//清理缓存
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
private Long productId;
public GetProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY)
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("GetProductInfoPool"))
//线程池大小,一般来说用默认的10就够了
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20)
//线程池缓冲队列大小
.withQueueSizeRejectionThreshold(10)));
this.productId = productId;
}
@Override
protected ProductInfo run() throws Exception {
String url = "http://localhost:8082/getProductInfo?productId=" + productId;
String response = HttpClientUtils.sendGetRequest(url);
System.out.println("调用接口,查询商品数据,productId="+productId);
return JSONObject.parseObject(response,ProductInfo.class);
}
@Override
protected String getCacheKey() {
return "product_info_" + this.productId;
}
public static void flushCache(Long productId) {
HystrixRequestCache.getInstance(KEY,HystrixConcurrencyStrategyDefault.getInstance()).clear("product_info_" + productId);
}
}
public class UpdateProductInfoCommand extends HystrixCommand<Boolean> {
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("UpdateProductInfoCommand");
private Long productId;
public UpdateProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY)
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("UpdateProductInfoPool"))
//线程池大小,一般来说用默认的10就够了
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(20)
//线程池缓冲队列大小
.withQueueSizeRejectionThreshold(10)));
this.productId = productId;
}
@Override
protected Boolean run() throws Exception {
//执行一次商品信息的更新
GetProductInfoCommand.flushCache(productId);
return true;
}
}